package hu.elte.inf.holiuml.model;

import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.io.Serializable;
import java.util.Vector;

import hu.elte.inf.holiuml.gui.CommentOptionsDialog;
import hu.elte.inf.holiuml.gui.HoliUML;
import hu.elte.inf.holiuml.gui.OptionsDialog;

import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

/**
 * @author Csaba Hoch
 */
abstract public class Diagram implements Drawable, Serializable {

	public enum EdgeType { RELATION, CONNECTION, COMMENT}

	/**
	 * The comment nodes of the diagram.
	 */
	protected Vector<CommentNode> commentNodes = new Vector<CommentNode>();
	protected Vector<CommentEdge> commentEdges = new Vector<CommentEdge>();

	protected String name = "Diagram";
	/**
	 * A reference to the main application.
	 */
	transient protected HoliUML main;

	/**
	 * The GUI uses this function to create the menu for the concrete diagram
	 * that the user edits. The menu is dynamic, because it can be different for
	 * different Diagrams: e.g. the ClassDiagram will contain a "New Class" menu
	 * item, while the Collaboration diagram will not. Each Diagram generates
	 * its own menu via this method.
	 * 
	 * @param menu
	 *            The menu that allows the user to perform valid actions on the
	 *            given diagram.
	 */
	public void createMenu(JMenu menu) {
		menu.add(new JMenuItem(new AbstractAction("New comment node") {
			public void actionPerformed(ActionEvent e) {
				createNewCommentNode();
			}
		}));
		menu.add(new JMenuItem(new AbstractAction("New comment edge") {
			public void actionPerformed(ActionEvent e) {
				createNewCommentEdge();
			}
		}));
	}

	/**
	 * Draws all the Drawables stored in the given Vector.
	 */
	public void drawDrawables(Graphics g, Vector<? extends Drawable> v) {
		for (Drawable drawable : v) {
			drawable.draw(g);
		}
	}

	/**
	 * Returns the node that is under the given point. Returns null if there is
	 * no node under the point.
	 */
	abstract public Node nodeUnderPoint(Point p);

	/**
	 * Returns the edge that is under the given point. Returns null if there is
	 * no edge under the point.
	 */
	abstract public Edge edgeUnderPoint(Point p);

	/**
	 * Returns the node that is under the given point in the given Vector.
	 * Returns null if there is no node under the point.
	 */
	protected Node nodeUnderPoint(Vector<? extends Node> v, Point p) {
		for (Node node : v) {
			if (node.inside(p)) {
				return node;
			}
		}
		return null;
	}

	/**
	 * Returns the edge that is under the given point in the given Vector.
	 * Returns null if there is no edge under the point.
	 */
	protected Edge edgeUnderPoint(Vector<? extends Edge> v, Point p) {
		for (Edge edge : v) {
			if (edge.inside(p)) {
				return edge;
			}
		}
		return null;
	}

	/**
	 * Creates a new comment node.
	 */
	private void createNewCommentNode() {
		CommentOptionsDialog commDlg = new CommentOptionsDialog(main, null);
		if (commDlg.getButtonCode() == OptionsDialog.OK) {
			main.getCanvas().getDiagram().addCommentNode(
					commDlg.createCommentNode());
			main.getCanvas().repaint();
		}
	}
	
	public void removeCommentNode(Node node) {
		Vector<CommentEdge> edges = getCommentEdgeByNode(node);
		for(int i = 0; i < edges.size(); i++){
			commentEdges.remove(edges.get(i));
		}
		commentNodes.remove(node);
		main.getCanvas().repaint();
	}
	
	public Vector<CommentEdge> getCommentEdgeByNode(Node node) {
		Vector<CommentEdge> comm = new Vector<CommentEdge>();
		for(CommentEdge e : commentEdges){
			if(e.getNodeBegin().equals(node) || e.getNodeEnd().equals(node)){
				comm.add(e);
			}
		}
		return comm;
	}

	/**
	 * Creates a new comment edge.
	 */
	private void createNewCommentEdge() {
		main.getCanvas().createNewEdge(EdgeType.COMMENT);
	}

	/**
	 * Creates a new edge between node1 and node2 depending on edgeType.
	 * 
	 * @param edgeType
	 *            The type of the edge. The value is meaningful for the concrete
	 *            diagram, who triggered the call of this function.
	 */
	public void createNewEdge(Node node1, Node node2, EdgeType edgeType) {
		if (edgeType == EdgeType.COMMENT) {
			getCommentEdges().add(new CommentEdge(node1, node2));
		}
	}
	public void removeCommentEdge(Edge edge) {
		if (edge.getClass().getSimpleName().equals("CommentEdge")) {
			getCommentEdges().remove((CommentEdge)edge);
			main.getCanvas().repaint();
		}
	}

	public void setMain(HoliUML main) {
		this.main = main;
	}

	public Vector<CommentNode> getCommentNodes() {
		return commentNodes;
	}

	public Vector<CommentEdge> getCommentEdges() {
		return commentEdges;
	}

	public void setCommentNodes(Vector<CommentNode> commentNodes) {
		this.commentNodes = commentNodes;
	}

	public void addCommentNode(CommentNode commentNode) {
		commentNodes.add(commentNode);
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
}
